home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
NR3.C
< prev
next >
Wrap
Text File
|
1993-08-09
|
29KB
|
1,077 lines
/* net/rom level 3 low level processing
* Copyright 1989 by Daniel M. Frank, W9NK. Permission granted for
* non-commercial distribution only.
*/
#include <stdio.h>
#include <ctype.h>
#include "global.h"
#include "config.h"
#ifdef NETROM
#include "mbuf.h"
#include "pktdrvr.h"
#include "iface.h"
#include "netuser.h"
#include "timer.h"
#include "arp.h"
#include "slip.h"
#include "ax25.h"
#include "netrom.h"
#include "nr4.h"
#include "socket.h"
#include "trace.h"
#include "ip.h"
#include "commands.h"
static int near nr_aliasck __ARGS((char *alias));
static int near accept_bc __ARGS((char *addr,unsigned ifnum));
static struct nr_bind * near find_best __ARGS((struct nr_bind *list,unsigned obso));
static struct nr_bind * near find_binding __ARGS((struct nr_bind *list,struct nrnbr_tab *neighbor));
static struct nrnbr_tab * near find_nrnbr __ARGS((char *, unsigned));
static struct nrnf_tab * near find_nrnf __ARGS((char *, unsigned));
static struct nr_bind * near find_worst __ARGS((struct nr_bind *list));
#ifdef XXX
static char *nr_getroute __ARGS((char *));
#endif
static struct raw_nr *Raw_nr;
struct nriface Nrifaces[NRNUMIFACE];
unsigned Nr_numiface = 0;
struct nrnbr_tab *Nrnbr_tab[NRNUMCHAINS];
struct nrroute_tab *Nrroute_tab[NRNUMCHAINS];
struct nrnf_tab *Nrnf_tab[NRNUMCHAINS];
unsigned Nr_nfmode = NRNF_NOFILTER;
unsigned short Nr_ttl = 15;
static unsigned Obso_init = 6;
static unsigned Obso_minbc = 5;
static unsigned Nr_maxroutes = 5;
unsigned Nr_autofloor = 10;
int Nr_verbose = 0;
int Nr_derate = 1; /* Allow automatic derating of routes */
int Nr_promisc = 0; /* behave promisuously with nr bcasts or not? */
struct iface *Nr_iface = NULLIF;
/* send a NET/ROM layer 3 datagram */
void
nr3output(char *dest,struct mbuf *data)
{
struct nr3hdr n3hdr;
struct mbuf *n3b;
memcpy(n3hdr.dest,dest,AXALEN); /* copy destination field */
n3hdr.ttl = Nr_ttl; /* time to live from initializer parm */
if((n3b = htonnr3(&n3hdr)) == NULLBUF){
free_p(data);
return;
}
append(&n3b, data);
/* The null interface indicates that the packet needs to have */
/* an appropriate source address inserted by nr_route */
nr_route(n3b,NULLAX25);
}
/* send IP datagrams across a net/rom network connection */
int
nr_send(struct mbuf *bp,struct iface *iface,int32 gateway,int prec,int del,int tput,int rel)
{
struct arp_tab *arp;
if((arp = arp_lookup(ARP_NETROM,gateway)) == NULLARP){
free_p(bp); /* drop the packet if no route */
return -1;
}
dump(iface,IF_TRACE_OUT,CL_NETROM,bp);
nr_sendraw(arp->hw_addr, NRPROTO_IP, NRPROTO_IP, bp);
return 0;
}
/* Send arbitrary protocol data on top of a NET/ROM connection */
void
nr_sendraw(char *dest,unsigned family,unsigned proto,struct mbuf *data)
{
struct mbuf *pbp;
struct nr4hdr n4hdr;
/* Create a "network extension" transport header */
n4hdr.opcode = NR4OPPID;
n4hdr.u.pid.family = family;
n4hdr.u.pid.proto = proto;
if((pbp = htonnr4(&n4hdr)) == NULLBUF){
free_p(data);
return;
}
append(&pbp,data); /* Append the data to that */
nr3output(dest, pbp); /* and pass off to level 3 code */
}
/* Arrange for receipt of raw NET/ROM datagrams */
struct raw_nr *
raw_nr(char protocol)
{
struct raw_nr *rp = mxallocw(sizeof(struct raw_nr));
rp->protocol = protocol;
rp->next = Raw_nr;
if(rp->next != NULLRNR)
rp->next->prev = rp;
Raw_nr = rp;
return rp;
}
/* Free a raw NET/ROM descriptor */
void
del_rnr(struct raw_nr *rpp)
{
struct raw_nr *rp;
/* Do sanity check on arg */
for(rp = Raw_nr; rp != NULLRNR; rp = rp->next)
if(rp == rpp)
break;
if(rp == NULLRNR)
return; /* Doesn't exist */
/* Unlink */
if(rp->prev != NULLRNR)
rp->prev->next = rp->next;
else
Raw_nr = rp->next;
if(rp->next != NULLRNR)
rp->next->prev = rp->prev;
/* Free resources */
free_q(&rp->rcvq);
xfree(rp);
}
/* Figure out if a call is assigned to one of my net/rom
* interfaces.
*/
int
ismyNcall(char *addr)
{
int i;
/* NREX bke 920717 */
for(i = 0; i < Nr_numiface; i++)
if(addreq(Nrifaces[i].call,addr)) /* was: [i].iface->hwaddr */
return (i + 1);
/* bke */
return 0;
}
/* Route net/rom network layer packets.
*/
void
nr_route(
struct mbuf *bp, /* network packet */
struct ax25_cb *iaxp) /* incoming ax25 control block */
{
struct nr3hdr n3hdr;
struct ax25_cb *axp;
struct mbuf *hbp, *pbp;
struct raw_nr *rnr;
struct nrnbr_tab *np;
struct nrroute_tab *rp;
struct nr_bind *bindp;
struct iface *iface;
struct ip ip;
char * nrcall; /* NREX bke 920717 */
if(ntohnr3(&n3hdr,&bp) == -1){
free_p(bp);
return;
}
/* If this isn't an internally generated network packet,
* give the router a chance to record a route back to the
* sender, in case they aren't in the local node's routing
* table yet.
*/
if(iaxp != NULLAX25){
unsigned ifnum;
/* find the interface number */
for(ifnum = 0; ifnum < Nr_numiface; ifnum++)
if(iaxp->iface == Nrifaces[ifnum].iface)
break;
if(ifnum == Nr_numiface){ /* Not a net/rom interface! */
free_p(bp);
return;
}
/* Add (possibly) a zero-quality recorded route via */
/* the neighbor from which this packet was received */
/* Note that this doesn't work with digipeated neighbors. */
nr_routeadd("######",n3hdr.source,ifnum,0,iaxp->path,0,1); /* NREX bke 920809 */
}
/* A packet from me, to me, can only be one thing:
* a horrible routing loop. This will probably result
* from a bad manual ARP entry, but we should fix these
* obscure errors as we find them.
*/
if(ismyNcall(n3hdr.dest)){
struct nr4hdr n4hdr;
/* Toss if from me, or if we can't read the header */
if(iaxp == NULLAX25 || ntohnr4(&n4hdr,&bp) == -1){
free_p(bp);
} else if((n4hdr.opcode & NR4OPCODE) == NR4OPPID){
for(rnr = Raw_nr; rnr != NULLRNR; rnr = rnr->next){
if(rnr->protocol != n4hdr.u.pid.family ||
rnr->protocol != n4hdr.u.pid.proto)
continue;
/* Duplicate the data portion, and put the
* level 3 header back on */
dup_p(&pbp,bp,0,len_p(bp));
if(pbp != NULLBUF && (hbp = htonnr3(&n3hdr)) != NULLBUF){
append(&hbp,pbp);
enqueue(&rnr->rcvq,hbp);
} else {
free_p(pbp);
free_p(hbp);
}
}
/* IP does not use a NET/ROM level 3 socket */
if(n4hdr.u.pid.family == NRPROTO_IP
&& n4hdr.u.pid.proto == NRPROTO_IP) {
struct arp_tab *arp;
dup_p(&pbp,bp,0,len_p(bp));
ntohip(&ip,&pbp);
free_p(pbp);
if((arp = arp_add(ip.source,ARP_NETROM,n3hdr.source,0,0)) != NULLARP) {
stop_timer(&arp->timer);
set_timer(&arp->timer,0L);
}
ip_route(iaxp->iface,Nr_iface,bp,0);
} else { /* we don't do this proto */
free_p(bp);
}
} else {
/* Must be net/rom transport: */
nr4input(&n4hdr,bp);
}
return;
}
/* L3RT bke 920712 920809 L3RTT-answer */
if (addreq(n3hdr.dest,Ax25multi[10]) && iaxp) {
if (n3hdr.ttl-1) { /* TTL n o t over? */
/* route back to origin! */
if ((rp = find_nrroute(n3hdr.source)) == NULLNRRTAB) {
/* can't happen, but who knows? */
free_p(bp);
return;
}
/* don't bother other neighbours even if they have a better */
/* quality to the sender of the L3RTT frame */
bindp = rp->routes;
while (bindp && !addreq(bindp->via->call, n3hdr.source))
bindp = bindp->next;
if (!bindp) {
free_p(bp);
return;
}
} else {
free_p(bp);
return;
}
} else {
if((rp = find_nrroute(n3hdr.dest)) == NULLNRRTAB) {
/* no route, drop the packet */
free_p(bp);
return;
}
if((bindp = find_best(rp->routes,1)) == NULLNRBIND) {
/* This shouldn't happen yet, but might if we add */
/* dead route detection */
free_p(bp);
return;
}
}
/* bke */
np = bindp->via;
iface = Nrifaces[np->iface].iface;
nrcall = Nrifaces[np->iface].call; /* NREX bke 920717 address of Net/ROM call */
/* Now check to see if iaxp is null. That is a signal that the packet
* originates here, so we need to insert the callsign of the appropriate
* interface
*/
/* NREX bke 920717 changed iface->hwaddr to nrcall */
if(iaxp == NULLAX25)
memcpy(n3hdr.source,nrcall,AXALEN);
/* bke */
if(--n3hdr.ttl == 0) { /* the packet's time to live is over! */
free_p(bp);
return;
}
/* Make sure there is a connection to the neighbor */
/* NREX bke 920717 the same as above: use nrcall, not hwaddr */
if((axp = find_ax25(np->call,nrcall)) == NULLAX25 ||
axp->state != CONNECTED){
/* Open a new connection or reinitialize old one */
/* hwaddr has been advanced to point to neighbor + digis */
if((axp = open_ax25(iface,nrcall,np->call, AX_ACTIVE,
s_arcall, s_atcall, s_ascall,-1)) == NULLAX25) {
free_p(bp);
return;
}
axp->user = 0;
}
/* now format network header */
if((pbp = htonnr3(&n3hdr)) == NULLBUF){
free_p(bp);
return;
}
append(&pbp,bp); /* append data to header */
/* put AX.25 PID on front */
bp = pushdown(pbp,1);
bp->data[0] = PID_NETROM;
if((pbp = segmenter(bp,iface->flags->paclen)) == NULLBUF){
free_p(bp);
return;
}
send_ax25(axp,pbp,DGRAM); /* pass it off to ax25 code */
}
/*
* Validate the alias field is good quality ascii to prevent network corruption
*/
static int near
nr_aliasck(char *alias)
{
int c, x = ALEN;
while (x--) {
c = *alias++;
if (!isprint( (int) c) )
return 1;
}
return 0;
}
/* Perform a nodes broadcast on interface # ifno in the net/rom
* interface table.
*/
void
nr_bcnodes(unsigned ifno,char *dest)
{
struct mbuf *dbp, *savehdr;
struct nrroute_tab *rp;
struct nrnbr_tab *np;
struct nr_bind * bp;
struct nr3dest nrdest;
int i, didsend = 0, numdest = 0;
char *cp;
struct iface *axif = Nrifaces[ifno].iface;
char *nrcall = Nrifaces[ifno].call; /* NREX bke 920717 */
/* prepare the header */
struct mbuf *hbp = alloc_mbuf(NR3NODEHL);
hbp->cnt = NR3NODEHL;
*hbp->data = NR3NODESIG;
memcpy(hbp->data+1,Nrifaces[ifno].alias,ALEN);
/* Some people don't want to advertise any routes; they
* just want to be a terminal node. In that case we just
* want to send our call and alias and be done with it.
*/
if(!Nr_verbose){
(*axif->output)(axif,dest,nrcall,PID_NETROM,hbp); /* NREX bke 920717 */
return;
}
/* make a copy of the header in case we need to send more than */
/* one packet */
savehdr = copy_p(hbp,NR3NODEHL);
/* now scan through the routing table, finding the best routes */
/* and their neighbors. create destination subpackets and append */
/* them to the header */
for(i = 0; i < NRNUMCHAINS; i++){
for(rp = Nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next){
/* look for best, non-obsolescent route */
if((bp = find_best(rp->routes,0)) == NULLNRBIND)
continue; /* no non-obsolescent routes found */
if(bp->quality == 0) /* this is a loopback route */
continue; /* we never broadcast these */
if (bp->quality < Nr_autofloor) /* below threshhold route */
continue ; /* so don't broadcast it */
if (rp->alias[0] == '#') /* NREX bke 920809 hidden node? */
continue ; /* forget it. */
if (nr_aliasck(rp->alias)) /* corrupted alias entry? */
continue ; /* don't rebroadcast it */
/* safety measure! */
np = bp->via;
/* insert best neighbor */
memcpy(nrdest.neighbor,np->call,AXALEN);
/* insert destination from route table */
memcpy(nrdest.dest,rp->call,AXALEN);
/* insert alias from route table */
strcpy(nrdest.alias,rp->alias);
/* insert quality from binding */
nrdest.quality = bp->quality;
/* create a network format destination subpacket */
if((dbp = htonnrdest(&nrdest)) == NULLBUF){
free_p(hbp); /* drop the whole idea ... */
free_p(savehdr);
return;
}
/* we now have a partially filled packet */
didsend = 0;
append(&hbp,dbp); /* append to header and others */
/* see if we have appended as many destinations
* as we can fit into a single broadcast. If we
* have, go ahead and send them out.
*/
if(++numdest == NRDESTPERPACK){ /* filled it up */
/* indicate that we did broadcast */
didsend = 1;
/* reset the destination counter */
numdest = 0;
(*axif->output)(axif,dest,nrcall,PID_NETROM,hbp);
/* new header */
hbp = copy_p(savehdr,NR3NODEHL);
}
}
}
/* Now, here is something totally weird. If our interfaces */
/* have different callsigns than this one, advertise a very */
/* high quality route to them. Is this a good idea? I don't */
/* know. However, it allows us to simulate a bunch of net/roms */
/* hooked together with a diode matrix coupler. */
for(i = 0; i < Nr_numiface; i++){
if(i == ifno)
continue; /* don't bother with ours */
cp = Nrifaces[i].call; /* NREX bke 920717 */
if(!addreq((char *)axif->hwaddr,cp)){
/* both destination and neighbor address */
memcpy(nrdest.dest,cp,AXALEN);
memcpy(nrdest.neighbor,cp,AXALEN);
/* alias of the interface */
strcpy(nrdest.alias,Nrifaces[i].alias);
/* and the very highest quality */
nrdest.quality = 255;
/* create a network format destination subpacket */
if((dbp = htonnrdest(&nrdest)) == NULLBUF){
free_p(hbp); /* drop the whole idea ... */
free_p(savehdr);
return;
}
/* we now have a partially filled packet */
didsend = 0;
/* append to header and others */
append(&hbp,dbp);
if(++numdest == NRDESTPERPACK){ /* filled it up */
/* indicate that we did broadcast */
didsend = 1;
/* reset the destination counter */
numdest = 0;
(*axif->output)(axif,dest,nrcall,PID_NETROM,hbp); /* send it *//* NREX bke 920717 */
/* new header */
hbp = copy_p(savehdr,NR3NODEHL);
}
}
}
/* If we have a partly filled packet left over, or we never */
/* sent one at all, we broadcast: */
if(!didsend || numdest > 0)
(*axif->output)(axif, dest, nrcall,PID_NETROM, hbp); /* NREX bke 920717 */
else {
if(numdest == 0) /* free the header copies */
free_p(hbp);
}
free_p(savehdr);
}
/* attach the net/rom interface. no parms for now. */
int
nr_attach(int argc,char *argv[],void *p)
{
if(Nr_iface != NULLIF) {
tprintf(Ifexist,"netrom");
return -1;
}
Nr_iface = mxallocw(sizeof(struct iface));
Nr_iface->addr = Ip_addr;
/* The strxdup is needed to keep the detach routine happy (it'll
* free the allocated memory)
*/
Nr_iface->name = strxdup("netrom");
if(Nr_iface->hwaddr == NULLCHAR)
Nr_iface->hwaddr = strxdup(Mycall);
Nr_iface->mtu = NR4MAXINFO;
setencap(Nr_iface,"NETROM");
Nr_iface->next = Ifaces;
Nr_iface->niface = 255; /* used for routing - DB3FL */
Ifaces = Nr_iface;
memcpy(Nr4user,Mycall,AXALEN);
return 0;
}
/* This function checks an ax.25 address and interface number against
* the filter table and mode, and returns -1 if the address is to be accepted
* verbatim, the quality if filtered in or 0 if it is to be filtered out.
*/
static int near
accept_bc(char *addr,unsigned ifnum)
{
struct nrnf_tab *fp = find_nrnf(addr,ifnum); /* look it up */
if(Nr_nfmode == NRNF_NOFILTER) /* no filtering in effect */
return -1;
if (fp != NULLNRNFTAB && Nr_nfmode == NRNF_ACCEPT)
return fp->quality ;
if (fp == NULLNRNFTAB && Nr_nfmode == NRNF_REJECT)
return -1;
return (Nr_promisc) ? -1 : 0;
}
/* receive and process node broadcasts. */
void
nr_nodercv(struct iface *iface,char *source,struct mbuf *bp)
{
int ifnum, qual;
char bcalias[AXALEN];
struct nr3dest ds;
/* First, see if this is even a net/rom interface: */
for(ifnum = 0; ifnum < Nr_numiface; ifnum++)
if(iface == Nrifaces[ifnum].iface)
break;
if(ifnum == Nr_numiface){ /* not in the interface table */
free_p(bp);
return;
}
if ((qual = accept_bc(source,ifnum)) == 0) { /* check against filter */
free_p(bp) ; /* and get quality */
return ;
}
/* See if it has a routing broadcast signature: */
if(PULLCHAR(&bp) != NR3NODESIG){
free_p(bp);
return;
}
/* now try to get the alias */
if(pullup(&bp,bcalias,ALEN) < ALEN){
free_p(bp);
return;
}
/* now check that the alias field is not corrupted - saftey measure! */
if (nr_aliasck(bcalias)) {
free_p(bp);
return;
}
bcalias[ALEN] = '\0'; /* null terminate */
/* enter the neighbor into our routing table */
if(qual == -1)
qual = Nrifaces[ifnum].quality; /* use default quality */
if(nr_routeadd(bcalias, source, ifnum, qual, source, 0, 0) == -1){
free_p(bp);
return;
}
/* we've digested the header; now digest the actual */
/* routing information */
while(ntohnrdest(&ds,&bp) != -1){
/* ignore routes to me! */
if(ismyNcall(ds.dest))
continue;
/* ignore routes with corrupted aliases - safety measure */
if (nr_aliasck (ds.alias))
continue;
/* ignore loopback paths to ourselves */
if(ismyNcall(ds.neighbor))
continue;
else
ds.quality = ((ds.quality * qual + 128) / 256) & 0xff;
/* ignore routes below the minimum quality threshhold */
if(ds.quality < Nr_autofloor)
continue;
if(nr_routeadd(ds.alias,ds.dest,ifnum,ds.quality,source,0,0) == -1)
break;
}
free_p(bp); /* This will free the mbuf if anything fails above */
}
/* The following are utilities for manipulating the routing table */
/* hash function for callsigns. Look familiar? */
int16
nrhash(char *s)
{
char x = 0;
int i;
for(i = ALEN; i !=0; i--)
x ^= *s++ & 0xfe;
x ^= *s & SSID;
return (int16)(uchar(x) % NRNUMCHAINS);
}
/* Find a neighbor table entry. Neighbors are determined by
* their callsign and the interface number. This takes care
* of the case where the same switch or hosts uses the same
* callsign on two different channels. This isn't done by
* net/rom, but it might be done by stations running *our*
* software.
*/
static struct nrnbr_tab * near
find_nrnbr(char *addr,unsigned ifnum)
{
struct nrnbr_tab *np;
int16 hashval = nrhash(addr);
/* search hash chain */
for(np = Nrnbr_tab[hashval]; np != NULLNTAB; np = np->next){
/* convert first in list to ax25 address format */
if(addreq(np->call,addr) && np->iface == ifnum){
return np;
}
}
return NULLNTAB;
}
/* Find a route table entry */
struct nrroute_tab *
find_nrroute(char *addr)
{
struct nrroute_tab *rp;
/* Find appropriate hash chain */
int16 hashval = nrhash(addr);
/* search hash chain */
for(rp = Nrroute_tab[hashval]; rp != NULLNRRTAB; rp = rp->next){
if(addreq(rp->call,addr)){
return rp;
}
}
return NULLNRRTAB;
}
/* Try to find the AX.25 address of a node with the given alias. Return */
/* a pointer to the AX.25 address if found, otherwise NULLCHAR. The alias */
/* should be a six character, blank-padded, upper-case string. */
char *
find_nralias(char *alias)
{
int i;
struct nrroute_tab *rp;
/* Since the route entries are hashed by ax.25 address, we'll */
/* have to search all the chains */
for(i = 0; i < NRNUMCHAINS; i++)
for(rp = Nrroute_tab[i]; rp != NULLNRRTAB; rp = rp->next)
if(strncmp(alias, rp->alias, 6) == 0)
return rp->call;
/* If we get to here, we're out of luck */
return NULLCHAR;
}
/* Find a binding in a list by its neighbor structure's address */
static struct nr_bind * near
find_binding(struct nr_bind *list,struct nrnbr_tab *neighbor)
{
struct nr_bind *bp;
for(bp = list; bp != NULLNRBIND; bp = bp->next)
if(bp->via == neighbor)
return bp;
return NULLNRBIND;
}
/* Find the worst quality non-permanent binding in a list */
static
struct nr_bind * near
find_worst(struct nr_bind *list)
{
struct nr_bind *bp, *worst = NULLNRBIND;
unsigned minqual = 1000; /* infinity */
for(bp = list; bp != NULLNRBIND; bp = bp->next)
if(!(bp->flags & NRB_PERMANENT) && bp->quality < minqual){
worst = bp;
minqual = bp->quality;
}
return worst;
}
/* Find the best binding of any sort in a list. If obso is 1,
* include entries below the obsolescence threshhold in the
* search (used when this is called for routing broadcasts).
* If it is 0, routes below the threshhold are treated as
* though they don't exist.
*/
static
struct nr_bind * near
find_best(struct nr_bind *list,unsigned obso)
{
struct nr_bind *bp, *best = NULLNRBIND;
int maxqual = -1; /* negative infinity */
for(bp = list; bp != NULLNRBIND; bp = bp->next)
if((int)bp->quality > maxqual)
if(obso || bp->obsocnt >= Obso_minbc){
best = bp;
maxqual = bp->quality;
}
return best;
}
/* Add a route to the net/rom routing table */
int
nr_routeadd(
char *alias, /* net/rom node alias, blank-padded and */
/* null-terminated */
char *dest, /* destination node callsign */
unsigned ifnum, /* net/rom interface number */
unsigned quality, /* route quality */
char *neighbor, /* neighbor node + 2 digis (max) in arp format */
unsigned permanent, /* 1 if route is permanent (hand-entered) */
unsigned record) /* 1 if route is a "record route" */
{
struct nrroute_tab *rp;
struct nr_bind *bp;
struct nrnbr_tab *np;
int16 rhash, nhash;
/* See if a routing table entry exists for this destination */
if((rp = find_nrroute(dest)) == NULLNRRTAB){
rp = mxallocw(sizeof(struct nrroute_tab));
/* create a new route table entry */
sprintf(rp->alias,"%.6s",alias);
addrcp(rp->call,dest);
rhash = nrhash(dest);
rp->next = Nrroute_tab[rhash];
if(rp->next != NULLNRRTAB)
rp->next->prev = rp;
Nrroute_tab[rhash] = rp; /* link at head of hash chain */
} else if(permanent || !strncmp(rp->alias,"##temp",6)) {
sprintf(rp->alias,"%.6s",alias); /* update the alias */
}
/* See if an entry exists for this neighbor */
if((np = find_nrnbr(neighbor,ifnum)) == NULLNTAB){
np = mxallocw(sizeof(struct nrnbr_tab));
/* create a new neighbor entry */
memcpy(np->call,neighbor,AXALEN);
np->iface = ifnum;
nhash = nrhash(neighbor);
np->next = Nrnbr_tab[nhash];
if(np->next != NULLNTAB)
np->next->prev = np;
Nrnbr_tab[nhash] = np;
} else if(permanent){ /* force this path to the neighbor */
memcpy(np->call,neighbor,AXALEN);
}
/* See if there is a binding between the dest and neighbor */
if((bp = find_binding(rp->routes,np)) == NULLNRBIND){
bp = mxallocw(sizeof(struct nr_bind));
/* create a new binding and link it in */
bp->via = np; /* goes via this neighbor */
bp->next = rp->routes; /* link into binding chain */
if(bp->next != NULLNRBIND)
bp->next->prev = bp;
rp->routes = bp;
rp->num_routes++; /* bump route count */
np->refcnt++; /* bump neighbor ref count */
bp->quality = quality;
bp->obsocnt = Obso_init; /* use initial value */
if(permanent)
bp->flags |= NRB_PERMANENT;
else if(record) /* notice permanent overrides record! */
bp->flags |= NRB_RECORDED;
} else {
if(permanent){ /* permanent request trumps all */
bp->quality = quality;
bp->obsocnt = Obso_init;
bp->flags |= NRB_PERMANENT;
bp->flags &= ~NRB_RECORDED; /* perm is not recorded */
} else if(!(bp->flags & NRB_PERMANENT)){ /* not permanent */
if(record){ /* came from nr_route */
if(bp->flags & NRB_RECORDED){ /* no mod non-rec bindings */
bp->quality = quality;
bp->obsocnt = Obso_init; /* freshen recorded routes */
}
} else { /* came from a routing broadcast */
bp->quality = quality;
bp->obsocnt = Obso_init;
bp->flags &= ~NRB_RECORDED; /* no longer a recorded route */
}
}
}
/* Now, check to see if we have too many bindings, and drop */
/* the worst if we do */
if(rp->num_routes > Nr_maxroutes){
/* since find_worst never returns permanent entries, the */
/* limitation on number of routes is circumvented for */
/* permanent routes */
if((bp = find_worst(rp->routes)) != NULLNRBIND){
nr_routedrop(dest,bp->via->call,bp->via->iface);
}
}
return 0;
}
/* Drop a route to dest via neighbor */
int
nr_routedrop(char *dest,char *neighbor,unsigned ifnum)
{
struct nrroute_tab *rp;
struct nrnbr_tab *np;
struct nr_bind *bp;
if((rp = find_nrroute(dest)) == NULLNRRTAB)
return -1;
if((np = find_nrnbr(neighbor,ifnum)) == NULLNTAB)
return -1;
if((bp = find_binding(rp->routes,np)) == NULLNRBIND)
return -1;
/* drop the binding first */
if(bp->next != NULLNRBIND)
bp->next->prev = bp->prev;
if(bp->prev != NULLNRBIND)
bp->prev->next = bp->next;
else
rp->routes = bp->next;
xfree(bp);
rp->num_routes--; /* decrement the number of bindings */
np->refcnt--; /* and the number of neighbor references */
/* now see if we should drop the route table entry */
if(rp->num_routes == 0){
if(rp->next != NULLNRRTAB)
rp->next->prev = rp->prev;
if(rp->prev != NULLNRRTAB)
rp->prev->next = rp->next;
else
Nrroute_tab[nrhash(dest)] = rp->next;
xfree(rp);
}
/* and check to see if this neighbor can be dropped */
if(np->refcnt == 0){
if(np->next != NULLNTAB)
np->next->prev = np->prev;
if(np->prev != NULLNTAB)
np->prev->next = np->next;
else
Nrnbr_tab[nrhash(neighbor)] = np->next;
xfree(np);
}
return 0;
}
#ifdef XXX
/* Find the best neighbor for destination dest, in arp format */
static char *
nr_getroute(char *dest)
{
struct nrroute_tab *rp;
struct nr_bind *bp;
if((rp = find_nrroute(dest)) == NULLNRRTAB)
return NULLCHAR;
if((bp = find_best(rp->routes,1)) == NULLNRBIND) /* shouldn't happen! */
return NULLCHAR;
return bp->via->call;
}
#endif /* notused */
/* Find an entry in the filter table */
static struct nrnf_tab * near
find_nrnf(char *addr,unsigned ifnum)
{
struct nrnf_tab *fp;
/* Find appropriate hash chain */
int16 hashval = nrhash(addr);
/* search hash chain */
for(fp = Nrnf_tab[hashval]; fp != NULLNRNFTAB; fp = fp->next){
if(addreq(fp->neighbor,addr) && fp->iface == ifnum){
return fp;
}
}
return NULLNRNFTAB;
}
/* Add an entry to the filter table. Return 0 on success,
* -1 on failure
*/
int
nr_nfadd(char *addr,unsigned ifnum,unsigned qual)
{
struct nrnf_tab *fp;
int16 hashval = nrhash(addr);
if(find_nrnf(addr,ifnum) != NULLNRNFTAB)
return 0; /* already there; it's a no-op */
fp = mxallocw(sizeof(struct nrnf_tab));
memcpy(fp->neighbor,addr,AXALEN);
fp->iface = ifnum;
fp->next = Nrnf_tab[hashval];
fp->quality = qual;
if(fp->next != NULLNRNFTAB)
fp->next->prev = fp;
Nrnf_tab[hashval] = fp;
return 0;
}
/* Drop a neighbor from the filter table. Returns 0 on success, -1
* on failure.
*/
int
nr_nfdrop(char *addr,unsigned ifnum)
{
struct nrnf_tab *fp;
if((fp = find_nrnf(addr,ifnum)) == NULLNRNFTAB)
return -1; /* not in the table */
if(fp->next != NULLNRNFTAB)
fp->next->prev = fp->prev;
if(fp->prev != NULLNRNFTAB)
fp->prev->next = fp->next;
else
Nrnf_tab[nrhash(addr)] = fp->next;
xfree(fp);
return 0;
}
/* called from lapb whenever a link failure implies that a particular ax25
* path may not be able to carry netrom traffic too well. Experimental!!!!
*/
void
nr_derate(struct ax25_cb *axp)
{
struct nrnbr_tab *np ;
struct nrroute_tab *rp;
struct nr_bind *bp;
struct mbuf *buf;
int i, ifnum, nr_traffic = 0; /* assume no netrom traffic on connection */
/* First, see if the derate funciton is enabled or
* this is even a net/rom interface:
*/
if (!Nr_derate || axp == NULLAX25)
return; /* abandon ship! */
/* If it is valid for netrom traffic, lets see if there is */
/* really netrom traffic on the connection to be derated. */
for (buf = axp->txq; buf != NULLBUF; buf = buf->anext)
if ((buf->data[0] & 0xff) == PID_NETROM)
nr_traffic = 1; /* aha - netrom traffic! */
if (!nr_traffic)
return; /* no sign of being used by netrom just now */
/* find the interface number */
for (ifnum = 0 ; ifnum < Nr_numiface ; ifnum++)
if (axp->iface == Nrifaces[ifnum].iface)
break ;
if (ifnum == Nr_numiface) /* may well happen! */
return ;
/* we now have the appropriate interface entry */
for (i = 0 ; i < NRNUMCHAINS ; i++) {
for (rp = Nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next) {
for (bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next) {
np = bp->via;
if(bp->quality >= 1 && np->iface == ifnum &&
!(bp->flags & NRB_PERMANENT) &&
!memcmp(np->call,axp->path + AXALEN,ALEN) &&
(np->call[6] & SSID) == ((axp->path + AXALEN)[6] & SSID)) {
bp->quality = ((bp->quality * 2) / 3);
}
}
}
}
}
#endif /* NETROM */